#include "StdAfx.h"
#include "SimpleMesh.h"
#include "util.h"


IDirect3DVertexDeclaration9* SimpleMesh::meshDecl = NULL;

// constructor
SimpleMesh::SimpleMesh(void) :
	indices(0),  
	vertices(NULL),
	nVertices(0), 
	nQuads(0), 
	nTriangles(0),
	nRows(0),
	nCols(0),
	bShowWireframe(false),
	texture(NULL)
{
	createVtxDescription();
}

// destructor
SimpleMesh::~SimpleMesh(void)
{
	// releae the com objects
	COM_RELEASE(this->meshDecl);
	COM_RELEASE(this->mp_VtxBuf);
	COM_RELEASE(this->mp_IndBuf);

	// free the memory
	FREE_MEMORY(vertices);
	FREE_MEMORY(indices);	
}

/******************************************************************
Descripton: creates a surface with numRows x NumCols vertices and (numRows-1) x (NumCols-1)
quads. Each quad size is dx x dz.
NOTE: The y axis represents the height and the mesh spans the x-z axis.
*******************************************************************/
SimpleMesh::SimpleMesh(int numRows, int numCols, float dx, float dz)  :
	indices(0), 
	vertices(NULL),
	nVertices(0), 
	nQuads(0), 
	nTriangles(0), 
	nRows(0), 
	nCols(0),
	bShowWireframe(false),
	texture(NULL)
{

	setScale(1.0, 1.0, 1.0);
	createVtxDescription();
	generateFlatSurface(numRows, numCols, dx, dz);
	setMaterial(1.0f, 1.0f, 1.0f);
}

/******************************************************************
Generates the surface - the vertices and the quads

Creates a surface with numRows x NumCols vertices and (numRows-1) x (NumCols-1)
quads. Each quad size is cellsize_x, cellsize_z.  

It allocates the required memory and store the vertices and quads with the object.  It also 
creats the required buffer on the graphics card.

The generated mesh is index based.
Note that the y axis represents the height and the mesh spans the x-z axis.
Return: 0 if success
******************************************************************/
int SimpleMesh::generateRandomSurface(int numRows, int numCols, float cellsize_x, float cellsize_z)
{	
	int rc = 0;
	int i,j,k;
	D3DXVECTOR3 rowPos(0.0,0.0,0.0);
	D3DXVECTOR3 colPos(0.0,0.0,0.0);
	float rangeMax = 10;
	float rangeMin = 0;

	nVertices = numRows*numCols;
	this->nRows = numRows;
	this->nCols = numCols;
	this->cellsize_x = cellsize_x;
	this->cellsize_z = cellsize_z;

	nQuads = (numRows-1) * (numCols-1);
	nTriangles = nQuads * 2;

	// allocate memory
	vertices = (struct DefaultVertex *) malloc(sizeof(struct DefaultVertex) * nVertices);
	if (vertices == NULL)
	{
		rc = 1;
		goto err;
	}

	indices = (long *) malloc(sizeof(long) * nTriangles * 3);
	if (indices == NULL)
	{
		rc = 1;
		goto err;
	}

	// fill the vertex buffer with positions
	k = 0;
	for(i = 0; i < numRows; i++)
	{
		// assign the pos vector for all comumns in a row
		colPos = rowPos;
		for(j = 0; j < numCols; j++)
		{
			// Negate the depth coordinate to put in quadrant four.  
			// Then offset to center about coordinate system.
			vertices[k].pos =  colPos;
			vertices[k].pos.y = (long)((double)rand()/(RAND_MAX+1) * (rangeMax - rangeMin) + rangeMin);
			vertices[k].color = D3DCOLOR_XRGB(255, 0, 0);
			colPos.x += cellsize_x;
			k++;
		}
		rowPos.z += cellsize_z;
	}


	// fill the index buffer
	k = 0;
	for(i = 0; i < numRows-1; i++)
	{
		for(j = 0; j < numCols-1; j++)
		{
			// fill indicesices for the quad
			indices[k++] = i * numCols + j;
			indices[k++] = i * numCols + j + 1;
			indices[k++] = (i+1) * numCols + j + 1;
			
			indices[k++] = i * numCols + j;
			indices[k++] = (i+1) * numCols + j + 1;
			indices[k++] = (i+1) * numCols + j;
		}
	}

	for( int i = 0; i < k; i += 3 )
	{
		// get the three vertices that make the faces
		DefaultVertex p1 = vertices[indices[i+0]];
		DefaultVertex p2 = vertices[indices[i+1]];
		DefaultVertex p3 = vertices[indices[i+2]];

		D3DXVECTOR3 u = p2.pos - p1.pos;
		D3DXVECTOR3 v = p3.pos - p1.pos;
		D3DXVECTOR3 normal = D3DXVECTOR3(u.y*v.z - u.z*v.y, u.z*v.x - u.x*v.z, u.x*v.y - u.y*v.x);

		D3DXVec3Normalize((D3DXVECTOR3*)&p1.normal, &normal);
		D3DXVec3Normalize((D3DXVECTOR3*)&p2.normal, &normal);
		D3DXVec3Normalize((D3DXVECTOR3*)&p3.normal, &normal);
	}

	createGraphicsBuffers(); // allocate memory in the graphics card memory for the vertices and the index buffer
	return(rc);
	
err:
	// clean up
	FREE_MEMORY(vertices);
	FREE_MEMORY(indices);
	return(rc);
}

/******************************************************************
Generates a flat surface.
Return: 0 if success
******************************************************************/
int SimpleMesh::generateFlatSurface(int numRows, int numCols, float cellsize_x, float cellsize_z)
{	
	int rc = 0;
	int i,j,k;
	D3DXVECTOR3 rowPos(0.0,0.0,0.0);
	D3DXVECTOR3 colPos(0.0,0.0,0.0);
	float rangeMax = 10;
	float rangeMin = 0;

	nVertices = numRows*numCols;
	this->nRows = numRows;
	this->nCols = numCols;
	this->cellsize_x = cellsize_x;
	this->cellsize_z = cellsize_z;

	nQuads = (numRows-1) * (numCols-1);
	nTriangles = nQuads * 2;

	// allocate memory
	vertices = (struct DefaultVertex *) malloc(sizeof(struct DefaultVertex) * nVertices);
	if (vertices == NULL)
	{
		rc = 1;
		goto err;
	}

	indices = (long *) malloc(sizeof(long) * nTriangles * 3);
	if (indices == NULL)
	{
		rc = 1;
		goto err;
	}

	// fill the vertex buffer with positions
	k = 0;
	float w = numRows * cellsize_x;
	float h = numRows * cellsize_z;
	for(i = 0; i < numRows; i++)
	{
		// assign the pos vector for all comumns in a row
		colPos = rowPos;
		for(j = 0; j < numCols; j++)
		{
			// Negate the depth coordinate to put in quadrant four.  
			// Then offset to center about coordinate system.
			vertices[k].pos =  colPos;
			vertices[k].pos.y = 0.0f;
			vertices[k].color = D3DCOLOR_XRGB(0, 255, 0);
			vertices[k].normal = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
			vertices[k].fU = (i*cellsize_x) / w;
			vertices[k].fV = (j*cellsize_z) / h;
			colPos.x += cellsize_x;
			k++;
		}
		rowPos.z += cellsize_z;
	}


	// fill the index buffer
	k = 0;
	for(i = 0; i < numRows-1; i++)
	{
		for(j = 0; j < numCols-1; j++)
		{
			// fill indicesices for the quad
			indices[k++] = i * numCols + j;
			indices[k++] = i * numCols + j + 1;
			indices[k++] = (i+1) * numCols + j + 1;
			
			indices[k++] = i * numCols + j;
			indices[k++] = (i+1) * numCols + j + 1;
			indices[k++] = (i+1) * numCols + j;
		}
	}
	createGraphicsBuffers(); // allocate memory in the graphics card memory for the vertices and the index buffer
	D3DXCreateTextureFromFile(md3dDev,
								"data/texture_grass.jpg",
								&texture);

	md3dDev->SetTexture(0,texture);
	return(rc);
	
err:
	// clean up
	FREE_MEMORY(vertices);
	FREE_MEMORY(indices);
	return(rc);
}


/******************************************************************
Creates the required index and vertex buffers on the graphics card
Return: 0 if success
******************************************************************/
int SimpleMesh::createGraphicsBuffers(void)
{
	int rc = 0;
	struct DefaultVertex *v = NULL;
	long *ind = NULL;

	rc =  md3dDev->CreateVertexBuffer(this->nVertices*sizeof(struct DefaultVertex), D3DUSAGE_WRITEONLY,
		DEFAULT_VERTEX_FVF, D3DPOOL_MANAGED, &this->mp_VtxBuf, NULL);

	// lock the buffer and copy the vertices (locking the entire array
	rc = mp_VtxBuf->Lock(0, 0, (void **) &v, 0);
	if (rc != D3D_OK) { goto err; }

	memcpy(v, this->vertices, nVertices*sizeof(struct DefaultVertex));
	rc = mp_VtxBuf->Unlock();
	rc = md3dDev->CreateIndexBuffer(this->nTriangles*3*sizeof(long), D3DUSAGE_WRITEONLY, D3DFMT_INDEX32, 
		D3DPOOL_MANAGED, &this->mp_IndBuf, NULL);

	// lock the buffer and copy the vertices (locking the entire array
	rc = mp_IndBuf->Lock(0, 0, (void **) &ind, 0);
	if (rc != D3D_OK) { goto err; }

	memcpy(ind, this->indices, nTriangles*3*sizeof(long));
	rc = mp_IndBuf->Unlock();
	if (rc != D3D_OK) { goto err; }

	return 0;

err:
	return(rc);
}


/******************************************************************
Creates the vertex description interface
Return: 0 if success
******************************************************************/
int SimpleMesh::createVtxDescription(void)
{
	int rc = 0;
	if (this->meshDecl == NULL) // create the vertex decalation
	{
		struct DefaultVertex v;
		D3DVERTEXELEMENT9 decl[] = 
		{{0,0,D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
		 {0,0,D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
		 {0,0,D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0},
		D3DDECL_END()};
		decl[0].Offset = (char *) &v.pos - (char *) &v;
		decl[1].Offset = (char *) &v.normal - (char *) &v;
		decl[2].Offset = (char *) &v.color - (char *) &v;
		rc = md3dDev->CreateVertexDeclaration(decl, &meshDecl);
	}
	return 0;
}

/******************************************************************
Purpose: Change the position of the surface.
Description: Set the position of hte surface.  The surface should be 
translated so the the vertex at (0,?,0) is moved to 
Return: 0 if success
******************************************************************/
int SimpleMesh::setCentrePosition(D3DXVECTOR3 centrePos)
{
	// to be udpated 
	this->m_Pos = centrePos;
	return 0;
}


void SimpleMesh::setMaterial(float r, float g, float b)
{
	ZeroMemory(&material, sizeof(D3DMATERIAL9));
	material.Diffuse.r = r;
	material.Diffuse.g = g;
	material.Diffuse.b = b;
	material.Diffuse.a = 1.0f;

	material.Specular.r = 255.0f;
	material.Specular.g = 255.0f;
	material.Specular.b = 255.0f;
	material.Power = 90.0f;
}

// render the mesh
int SimpleMesh::render(int time)
{
	D3DXMATRIX worldMat, viewMat, matTransform, matProjection, matScale, matTranslate,  matRotation;
	D3DXMatrixScaling(&matScale, m_ScaleX, m_ScaleY, m_ScaleZ);
	worldMat = matScale;

	D3DXMatrixTranslation(&matTranslate, m_Pos.x, m_Pos.y, m_Pos.z);
	worldMat *= matTranslate;

	md3dDev->SetTransform(D3DTS_WORLD, &worldMat);

	// set the source
	md3dDev->SetMaterial( &material );
	md3dDev->SetTexture(0,texture);
	md3dDev->SetStreamSource( 0, mp_VtxBuf, 0, sizeof(DefaultVertex) );
	md3dDev->SetIndices(mp_IndBuf); // set the index buffer

	md3dDev->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, this->nVertices, 0, this->nTriangles);
	return 0;
}


/******************************************************************
Purpose: Sets the surface scaling factor
Descripton: set the position of hte surfce.  The surface should be 
translated so the the vertex at (0,?,0) is moved to 
******************************************************************/
int SimpleMesh::setScale(float scaleX, float scaleY, float scaleZ)
{
	this->m_ScaleX = scaleX;
	this->m_ScaleY = scaleY;
	this->m_ScaleZ = scaleZ;
	return 0;
}
